import React from 'react';
import classNames from 'classnames';
import Validation from '../utils/Validation';
import Label from '../Input/Label';
import './FormItem.less';

class FormItem extends React.Component {
    displayName = 'FormItem';
    static displayName = 'FormItem';

    state = {
        valid: true
    }

    bindItem = (f) => {
        this.item = f;
    }

    componentDidMount () {
        this.props.itemBind && this.props.itemBind(this, this.props.name);
    }

    componentWillUnmount () {
        this.props.itemUnBind && this.props.itemUnBind(this.props.name);
    }

    async onChange () {
        const args = Array.prototype.slice.call(arguments);
        const v = args[0];
        const checkRet = await this.check(v);
        
        if (checkRet) {
            this.msg = null;
            this.method = null;
            this.setState({
                valid: true
            });
        } else {
            this.setState({
                valid: false
            });
        }
        if (this.props.onChange) {
            this.props.onChange.apply(this, args);
        }
    }

    async _check (value) {
        const checkRet = await this.check(value);
        if (checkRet) {
            this.msg = null;
            this.method = null;
            this.setState({
                valid: true
            });
        } else {
            this.setState({
                valid: false
            });
        }
        return checkRet;
    }

    async check (value) {
        const {required} = this.props;
        if (this.item.isValid) {
            const ret = await this.item.isValid();
            if (typeof ret !== 'object') {
                console.error(`${this.props.name} isValid 方法验证结果需要返回格式{valid: false, msg: ''}`);
                this.msg = `${this.props.name} isValid 方法验证结果格式错误`;
                return false;
            } else {
                this.msg = ret.msg;
                return ret.valid;
            }
        } else {
            const rules = this.props.rules || {};
            if (required) {
                rules['required'] = true;
            }
            if (!rules['required'] && (value === null || value === '' || value === undefined)) {
                if (!this.state.valid) {
                    return true;
                }
            }
            if (rules['required']) {
                const rule = { method: 'required', parameters: rules[ 'required' ] };
                const result = await this.validByMethod(value, rule);
                if (!result) {
                    return result;
                }
            }

            for (const method in rules) {
                if (method === 'required' || method === 'remote') {
                    continue;
                }
                const rule = { method, parameters: rules[ method ] };
    
                const result = await this.validByMethod(value, rule);
                if (!result) {
                    return false;
                }
            }
            return true;
        }
    }

    async validByMethod (value, rule) {
        let method = rule.method;
        if (typeof rule.parameters === 'function') {
            method = rule.parameters;
            this.method = rule.method;
            return await method.call(this, value, this.props.pojo);
        } else {
            if (!Validation.methods[ method ]) {
                console.error(`验证中缺少${method}方法`);
                return {
                    valid: true
                };
            }
            this.method = method;
            this.ruleParams = rule.parameters;
            return await Validation.methods[ method ].call(this, value, rule.parameters, this.props.pojo);
        }
    }

    getFormItem () {
        let {type} = this.props;
        type = type || 'text';
        let component = null;
        component = FormItem.COMPONENTS[type];
        
        if (!component) {
            component = FormItem.COMPONENTS['text'];
        }
        const props = Object.assign({
            ref: this.bindItem,
            className: this.props.itemClass,
            style: this.props.itemStyle
        }, this.props);
        props.onChange = this.onChange.bind(this);
        delete props['itemClass'];
        delete props['itemStyle'];
        delete props['className'];
		delete props['itemBind'];
        delete props['itemUnBind'];
        delete props['labelWidth'];

        return React.createElement(component.component, props);
    }

    getMessage () {
        if (this.msg) {
            return this.msg;
        } else {
            const messages = this.props.messages;
            if (this.method) {
                const msg = (messages && messages[this.method]) ? messages[this.method] : Validation.messages[this.method];
                if (typeof msg === 'function') {
                    let params = this.ruleParams;
                    if (!(params instanceof Array)) {
                        params = [params];
                    }
                    return msg(...params);
                } else {
                    return msg;
                }
            } else {
                return null;
            }
        }
    }

    render () {
        let {className, label, labelWidth, labelStyle, layout, style, required, gutter} = this.props;
        className = classNames('cm-form-item', className, {
            'cm-form-item-required': required,
            [`cm-form-item-layout-${layout}`]: layout,
            'cm-form-item-invalid': !this.state.valid
        });
        const labelClass = classNames('cm-from-label', {
            'cm-form-label-required': required
        });

        const element = this.getFormItem();
        labelStyle = Object.assign({width: labelWidth}, labelStyle || {});
        const labelEle = label === '' ? '' : <Label className={labelClass} style={labelStyle} value={label} />;
        const tip = this.getMessage();
        return <div className={className} style={Object.assign({}, style, gutter !== undefined ? {marginBottom: gutter} : {})}>
            {labelEle}
            <div className='cm-form-item-content' style={{marginLeft: labelWidth ? labelWidth : 'auto'}}>
                {element}
                <div className='cm-form-item-error-tip'>{tip}</div>
            </div>
        </div>;
    }
}

FormItem.COMPONENTS = {

};

/**
 * 注册空间到FormItem
 * @param component 空间类
 * @param type 空间类型
 * @param valueType 值类型
 */
FormItem.register = function (component, type, valueType) {
    if (type instanceof Array) {
        type.forEach((theType) => {
            if (theType === 'number' || theType === 'integer' || theType === 'tel') {
                valueType = 'number';
            }
            if (!FormItem.COMPONENTS[theType]) {
                FormItem.COMPONENTS[theType] = {
                    component,
                    valueType: valueType || 'string'
                };
            }
        });
    } else {
        if (!FormItem.COMPONENTS[type]) {
            FormItem.COMPONENTS[type] = {
                component,
                valueType: valueType || 'string'
            };
        }
    }
};

export default FormItem;
